package cn.elves.model.uua.mult.authenticator.sms;


import cn.elves.model.entity.dto.UserDTO;
import cn.elves.model.entity.dto.response.ResponseDTO;
import cn.elves.model.entity.vo.Result;
import cn.elves.model.provider.UserProvider;
import cn.elves.model.provider.VerificationCodeProvider;
import cn.elves.model.uua.exception.AuthErrorType;
import cn.elves.model.uua.mult.MultAuthentication;
import cn.elves.model.uua.mult.authenticator.AbstractPreparableMultAuthenticator;
import cn.elves.model.uua.mult.authenticator.sms.event.SmsAuthenticateBeforeEvent;
import cn.elves.model.uua.mult.authenticator.sms.event.SmsAuthenticateSuccessEvent;
import cn.elves.model.uua.mult.authenticator.sms.exception.SmsValidateException;
import cn.elves.model.uua.mult.authenticator.sms.result.SmsCodeValidateResult;
import cn.elves.model.uua.mult.authenticator.sms.result.SmsValidateResultContext;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 短信验证码集成认证
 **/
@Slf4j
@Component
public class SmsMultAuthenticator extends AbstractPreparableMultAuthenticator implements ApplicationEventPublisherAware {

    @Resource
    private UserProvider userProvider;

    @Resource
    private VerificationCodeProvider verificationCodeProvider;


    @Resource
    private PasswordEncoder passwordEncoder;

    private ApplicationEventPublisher applicationEventPublisher;

    private final static String SMS_AUTH_TYPE = "SMS";

    @Override
    public UserDTO authenticate(MultAuthentication multAuthentication) {
        SmsCodeValidateResult validateResult = SmsValidateResultContext.get();
        if (null != validateResult && !validateResult.isSuccess()) {
            AuthErrorType authErrorType = validateResult.getAuthErrorType();
            log.info("短信验证码错误,手机号:{}, 结果:{}", multAuthentication.getUsername(), authErrorType);
            throw new OAuth2Exception(authErrorType.getMesg());
        }

        //获取密码，实际值是验证码
        String password = multAuthentication.getAuthParameter("password");
        //获取用户名，实际值是手机号
        String username = multAuthentication.getUsername();
        //发布事件，可以监听事件进行自动注册用户
        this.applicationEventPublisher.publishEvent(new SmsAuthenticateBeforeEvent(multAuthentication));
        //通过手机号码查询用户
        UserDTO userDTO = userProvider.getUserByPhone(username).getContent();
        if (userDTO != null) {
            //将密码设置为验证码
            userDTO.setPassword(passwordEncoder.encode(password));
            //发布事件，可以监听事件进行消息通知
            this.applicationEventPublisher.publishEvent(new SmsAuthenticateSuccessEvent(multAuthentication));
        }
        return userDTO;
    }

    @Override
    public void prepare(MultAuthentication multAuthentication, HttpServletResponse response) {

        String smsCode = multAuthentication.getAuthParameter("password");
        String username = multAuthentication.getAuthParameter("username");
        ResponseDTO result = verificationCodeProvider.checkCode(username, smsCode);
        if (null == result || !result.isSuccess()) {

            SmsCodeValidateResult validateResult = new SmsCodeValidateResult();
            validateResult.setSuccess(false);
            validateResult.setAuthErrorType(AuthErrorType.ERROR_VERIFY_CODE);
            SmsValidateResultContext.set(validateResult);
//             直接写回响应
            ResponseEntity.status(HttpStatus.OK).body(AuthErrorType.ERROR_VERIFY_CODE);
            PrintWriter writer = null;
            try {
                writer = response.getWriter();
            } catch (IOException e) {
                e.printStackTrace();
            }
            response.setContentType("application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            Result fail = Result.fail(AuthErrorType.valueOf(AuthErrorType.ERROR_VERIFY_CODE.getMesg().toUpperCase()), AuthErrorType.ERROR_VERIFY_CODE);
            writer.write(JSON.toJSONString(fail));
            writer.flush();
            writer.close();

            throw new SmsValidateException(validateResult);
        }
    }


    @Override
    public boolean support(MultAuthentication multAuthentication) {
        return SMS_AUTH_TYPE.equals(multAuthentication.getAuthType());
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}
